﻿#include "ffmpegvideo.h"
#include <QDebug>
#include <QThread>
#include "gif/gif.h"

FFmpegVideo::FFmpegVideo(){

}

FFmpegVideo::~FFmpegVideo(){

}

bool FFmpegVideo::loadVideoFile(QString filename){

    this->m_filename = filename;
    char* error_info = new char[32];     //异常信息

    // 获取视频文件信息
    avformat_context = avformat_alloc_context();  // 开辟空间
    int avformat_open_result = avformat_open_input(&avformat_context,
                                                   filename.toStdString().c_str(), nullptr, nullptr);
    if (avformat_open_result != 0){
        av_strerror(avformat_open_result, error_info, 100);
        qDebug()<<QString("获取视频文件格式失败 %1").arg(error_info);
        return false;
    };

    // 获取视频流
    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, nullptr);
    if (avformat_find_stream_info_result < 0){
        av_strerror(avformat_find_stream_info_result, error_info, 100);
        qDebug()<<QString("获取音视频流失败 %1").arg(error_info);
    }
    av_stream_index = -1;
    for (quint8 i = 0; i < avformat_context->nb_streams; i++){  //循环遍历每一流找到视频流
        if (avformat_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){

            av_stream_index = i;  // 则streams[av_stream_index]是视频流
            break;
        }
    }
    if (av_stream_index == -1){
        qDebug()<<QString("没有找到视频流");
        return false;
    }

    // 获取解码器
    const AVCodec* avcodec = avcodec_find_decoder(avformat_context->streams[av_stream_index]->codecpar->codec_id);
    if (avcodec == nullptr){
        qDebug()<<QString("没有找到视频解码器");
        return false;
    }
    avcodec_context = avcodec_alloc_context3(avcodec);
    if (avcodec_parameters_to_context(avcodec_context, avformat_context->streams[av_stream_index]->codecpar) < 0){
        qDebug()<<"解码器拷贝参数失败";
        return false;
    }
    int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec, nullptr);
    if (avcodec_open2_result != 0){
        av_strerror(avformat_find_stream_info_result, error_info, 100);
        qDebug()<<QString("打开解码器失败 %1").arg(error_info);
        return false;
    }

    /// 视频信息输出
    AVRational framerate = avcodec_context->framerate;
    this->m_fps = framerate.num / framerate.den;
    this->m_frame_num = avformat_context->streams[av_stream_index]->nb_frames;
    this->m_frame_timestamp = avformat_context->duration / m_frame_num;
    qDebug()<<"总帧数："<<avformat_context->streams[av_stream_index]->nb_frames;
    qDebug()<<"帧率"<<this->m_fps;
    qDebug()<<"视频详细信息输出";
    qDebug()<<"视频时长/s"<<avformat_context->duration/1000000;    // 微妙us
    qDebug()<<"一帧所占时间/us"<<this->m_frame_timestamp;
    qDebug()<<QString("视频分辨率%1x%2").arg(avcodec_context->width).arg(avcodec_context->height);

    this->m_stopPlay = false;
    this->m_frame_id = 0;

    return true;
}

void FFmpegVideo::play(){
    qDebug() << "FFmpegVideo::play()";

    // 初始化临时变量
    AVPacket* av_packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket)));
    AVFrame *pFramein = av_frame_alloc();   //输入和输出的帧数据
    AVFrame *pFrameRGB = av_frame_alloc();
    uint8_t * pOutbuffer = static_cast<uint8_t *>(av_malloc(      //缓冲区分配内存
                           static_cast<quint64>(
                           av_image_get_buffer_size(AV_PIX_FMT_RGB32,
                                                    avcodec_context->width,
                                                    avcodec_context->height,
                                                    1))));
    // 初始化缓冲区
    av_image_fill_arrays(pFrameRGB->data,
                         pFrameRGB->linesize,
                         pOutbuffer,
                         AV_PIX_FMT_RGB32,
                         avcodec_context->width, avcodec_context->height, 1);

    // 格式转换
    SwsContext* pSwsContext = sws_getContext(avcodec_context->width,    // 输入宽
                                             avcodec_context->height,   // 输入高
                                             avcodec_context->pix_fmt,  // 输入格式
                                             avcodec_context->width,    // 输出宽
                                             avcodec_context->height,   // 输出高
                                             AV_PIX_FMT_RGB32,          // 输出格式
                                             SWS_BICUBIC,               ///todo
                                             nullptr,
                                             nullptr,
                                             nullptr);

    int ret=0;
    this->m_stopPlay = false;
    //this->m_frame_id = 0;

    // 开始循环
    while (this->m_stopPlay == false){
        //从视频文件上下文中读取包--- 有数据就一直读取
        if (av_read_frame(avformat_context, av_packet) >= 0){
            //解码什么类型流(视频流、音频流、字幕流等等...)
            if (av_packet->stream_index == av_stream_index){
                avcodec_send_packet(avcodec_context, av_packet);        // 解码
                qDebug()<<QString("当前第 %1 帧时间：%2 (%3)").arg(m_frame_id).arg(av_packet->pts * av_q2d(avformat_context->streams[av_stream_index]->time_base)).arg(av_packet->flags);
                ret = avcodec_receive_frame(avcodec_context, pFramein); // 获取解码输出
                if (ret == 0){
                    sws_scale(pSwsContext,  //图片格式的转换
                              static_cast<const uint8_t* const*>(pFramein->data),
                              pFramein->linesize, 0, avcodec_context->height,
                              pFrameRGB->data,  pFrameRGB->linesize);

                    QImage  *tmpImg  = new QImage(static_cast<uchar *>(pOutbuffer),
                                                  avcodec_context->width,
                                                  avcodec_context->height,
                                                  QImage::Format_RGB32);
                    QImage image = tmpImg->copy();
                    this->m_frame_id++;            //计数第几帧
                    emit sig_SendOneFrame(image);  //发送图片信号
                    emit sig_SendFrameNum_play(this->m_frame_id);
                    QThread::msleep(quint32(1000/this->m_fps));  //延时操作  1秒显示fps帧
                    //qDebug()<<QString("当前遍历第 %1 帧").arg(m_frame_id);
                }
            }
        }
        else{
            qDebug()<<"播放完毕";
            this->m_frame_id = 0;
            this->m_stopPlay = true;
        }
        av_packet_unref(av_packet);
    }
}

void FFmpegVideo::savetoGif()
{
    qDebug()<<"savetoGif";
    //avformat_seek_file()
    GifWriter writer = {};
    GifBegin(&writer, this->m_outfilename.toStdString().c_str(),
             static_cast<uint32_t>(avcodec_context->width),
             static_cast<uint32_t>(avcodec_context->height),
             static_cast<uint32_t>(100/this->m_fps),        // 单位是1/100秒，即10ms
             8, true );
    // 初始化临时变量
    AVPacket* av_packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket)));
    AVFrame *pFramein = av_frame_alloc();   //输入和输出的帧数据
    AVFrame *pFrameRGB = av_frame_alloc();
    uint8_t * pOutbuffer = static_cast<uint8_t *>(av_malloc(      //缓冲区分配内存
                           static_cast<quint64>(
                           av_image_get_buffer_size(AV_PIX_FMT_RGBA,
                                                    avcodec_context->width,
                                                    avcodec_context->height,
                                                    1))));
    // 初始化缓冲区
    av_image_fill_arrays(pFrameRGB->data,
                         pFrameRGB->linesize,
                         pOutbuffer,
                         AV_PIX_FMT_RGB32,
                         avcodec_context->width, avcodec_context->height, 1);

    // 格式转换
    SwsContext* pSwsContext = sws_getContext(avcodec_context->width,    // 输入宽
                                             avcodec_context->height,   // 输入高
                                             avcodec_context->pix_fmt,  // 输入格式
                                             avcodec_context->width,    // 输出宽
                                             avcodec_context->height,   // 输出高
                                             AV_PIX_FMT_RGBA,           // 输出格式
                                             SWS_BICUBIC,               ///todo
                                             nullptr,
                                             nullptr,
                                             nullptr);

    int ret=0;
    int frame_id = 0;
    this->m_stopProcessing = false;

    // 开始循环
    while(this->m_stopProcessing == false){
        if (av_read_frame(avformat_context, av_packet) >= 0){
            if (av_packet->stream_index == av_stream_index){
                avcodec_send_packet(avcodec_context, av_packet);        // 解码
                ret = avcodec_receive_frame(avcodec_context, pFramein); // 获取解码输出
                if (ret == 0){
                    sws_scale(pSwsContext,  //图片格式的转换
                              static_cast<const uint8_t* const*>(pFramein->data),
                              pFramein->linesize, 0, avcodec_context->height,
                              pFrameRGB->data,  pFrameRGB->linesize);

                    QImage  *tmpImg  = new QImage(static_cast<uchar *>(pOutbuffer),
                                                  avcodec_context->width,
                                                  avcodec_context->height,
                                                  QImage::Format_RGBA8888);
                    QImage image = tmpImg->copy();
                    GifWriteFrame(&writer, image.bits(),
                                  static_cast<uint32_t>(avcodec_context->width),
                                  static_cast<uint32_t>(avcodec_context->height),
                                  static_cast<uint32_t>(100/this->m_fps),        // 单位是1/100秒，即10ms
                                  8, true);
                    frame_id++;
                    qDebug()<<QString("当前转换第 %1 帧").arg(frame_id);
                    emit sig_SendFrameNum(frame_id);
                    //break;
                }
            }
        }
    }
    GifEnd(&writer);   // 取消之后是否需要保存不完整的gif?  暂时保存
    av_packet_unref(av_packet);
}

void FFmpegVideo::JumptotheFrame(qint64 min_frame_id, qint64 frame_id, qint64 max_frame_id)
{  
    // 将帧号转换为时间戳
    int64_t min_ts = min_frame_id * this->m_frame_timestamp;
    int64_t ts = frame_id * this->m_frame_timestamp;
    int64_t max_ts = max_frame_id * this->m_frame_timestamp;

    qDebug()<<"跳转到：" << ts/1000000.0 << "s";
    // this->av_stream_index
    // avformat_seek_file(this->avformat_context, -1, min_ts, ts, max_ts, AVSEEK_FLAG_FRAME);
    avformat_seek_file(this->avformat_context, -1, min_ts, ts, max_ts, AVSEEK_FLAG_ANY);
}
